package com.hzqc.wxaapp.sdk;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.IOException;
import java.net.URI;
import java.util.Collections;

/**
 * The type Abstract wechat app service.
 *
 * @author n1
 * @since 2021 /6/18 10:18
 */
public abstract class AbstractWechatAppService {

    private final RestTemplate restTemplate;

    public AbstractWechatAppService(WechatAppTokenService wechatAppTokenService) {
        this.restTemplate = wechatAppTokenService.getRestTemplate();
        this.restTemplate
                .setInterceptors(Collections.singletonList(new TokenClientHttpRequestInterceptor(wechatAppTokenService)));
    }

    /**
     * Client rest template.
     *
     * @return the rest template
     */
    RestTemplate client(String clientId) {
        Assert.hasText(clientId, "clientId is required");
        ClientHolder.setClient(clientId);
        return this.restTemplate;
    }


    private static class ClientHolder {
        private static final ThreadLocal<String> HOLD = new ThreadLocal<>();

        static void setClient(String clientId) {
            HOLD.set(clientId);
        }

        static String getClient() {
            return HOLD.get();
        }

        static void clear() {
            HOLD.remove();
        }
    }

    /**
     * The type Token client http request interceptor.
     */
    static class TokenClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
        private final WechatAppTokenService wechatAppTokenService;


        public TokenClientHttpRequestInterceptor(WechatAppTokenService wechatAppTokenService) {
            this.wechatAppTokenService = wechatAppTokenService;

        }

        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {

            try {
                MutableHttpRequest mutableHttpRequest = new MutableHttpRequest(request);
                URI uri = UriComponentsBuilder.fromUri(request.getURI())
                        .queryParam("access_token", wechatAppTokenService.getToken(ClientHolder.getClient()))
                        .build()
                        .toUri();
                mutableHttpRequest.setUri(uri);

                return execution.execute(mutableHttpRequest, body);
            } finally {
                ClientHolder.clear();
            }
        }


        private static class MutableHttpRequest implements HttpRequest {

            private final HttpRequest source;
            private String methodValue;
            private URI uri;
            private HttpHeaders headers;

            /**
             * Instantiates a new Mutable http request.
             *
             * @param source the source
             */
            public MutableHttpRequest(HttpRequest source) {
                this.source = source;
                this.methodValue = source.getMethodValue();
                this.uri = source.getURI();
                this.headers = source.getHeaders();
            }

            /**
             * Sets method value.
             *
             * @param methodValue the method value
             */
            public void setMethodValue(String methodValue) {
                this.methodValue = methodValue;
            }

            /**
             * Sets uri.
             *
             * @param uri the uri
             */
            public void setUri(URI uri) {
                this.uri = uri;
            }

            /**
             * Sets headers.
             *
             * @param headers the headers
             */
            public void setHeaders(HttpHeaders headers) {
                this.headers = headers;
            }

            @Override
            public String getMethodValue() {
                return this.methodValue;
            }

            @Override
            public URI getURI() {
                return this.uri;
            }

            @Override
            public HttpHeaders getHeaders() {
                return this.headers;
            }

            /**
             * Gets source.
             *
             * @return the source
             */
            public HttpRequest getSource() {
                return source;
            }
        }
    }
}
